package com.bachongLightNovel.works.service.impl;

import com.alibaba.fastjson2.JSON;
import com.bachongLightNovel.constant.RedisConstant;
import com.bachongLightNovel.error.ErrorCodeEnum;
import com.bachongLightNovel.exception.CustomException;
import com.bachongLightNovel.model.dto.audit.AuditNovelDto;
import com.bachongLightNovel.model.pojo.system.User;
import com.bachongLightNovel.model.pojo.works.Novel;
import com.bachongLightNovel.model.pojo.works.NovelDetails;
import com.bachongLightNovel.model.vo.audit.AuditNovelVo;
import com.bachongLightNovel.model.vo.works.NovelRecordVo;
import com.bachongLightNovel.model.vo.works.NovelVo;
import com.bachongLightNovel.utils.GlobalIdWorker;
import com.bachongLightNovel.utils.UserInfoUtil;
import com.bachongLightNovel.works.config.RabbitmqConfig;
import com.bachongLightNovel.works.mapper.WorksNovelDetailMapper;
import com.bachongLightNovel.works.mapper.WorksNovelMapper;
import com.bachongLightNovel.works.service.WorksNovelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import cn.hutool.core.util.StrUtil;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

/**
 * @author vermouth
 */
@Service
@Slf4j
public class WorksNovelServiceImpl implements WorksNovelService {

    //注入全局id生成器
    @Autowired
    private GlobalIdWorker globalIdWorker;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private WorksNovelMapper worksNovelMapper;

    @Autowired
    private WorksNovelDetailMapper worksNovelDetailMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 新建小说基本信息
     * @param novel :
     * @return Result
     */
    @Override
    @Transactional
    public void saveNovel(Novel novel) {
        //数据校验
        if (StrUtil.isBlank(novel.getName())) {
            throw new CustomException(ErrorCodeEnum.NOVEL_NAME_ILLEGAL);
        }
        if (StrUtil.isBlank(novel.getImage())) {
            throw new CustomException(ErrorCodeEnum.NOVEL_IMAGE_ILLEGAL);
        }
        if (StrUtil.isBlank(novel.getDescription())) {
            throw new CustomException(ErrorCodeEnum.NOVEL_DESCRIPTION_ILLEGAL);
        }
        //生成小说id，补全相应属性
        long novelId = globalIdWorker.generateId(RedisConstant.BACHONG_ID_NOVEL);
        //获取当前用户id和用户名
        User curUser = UserInfoUtil.getUser();
        Long userId = curUser.getId();
        String username = curUser.getUsername();
        novel.setUserId(userId);
        novel.setAuthor(username);
        novel.setId(novelId);
        novel.setStatus((short) 0);
        novel.setCreateTime(LocalDateTime.now());
        //将小说基本信息存入到redis当中
        String key = RedisConstant.BACHONG_NOVEL_INFO + ":" + userId;
        String novelJson = JSON.toJSONString(novel);
        stringRedisTemplate.opsForZSet().add(key, novelJson, novelId);
        //插入到数据库当中
        worksNovelMapper.insert(novel);
    }

    /**
     * 查询当前登录用户的小说集合
     * @return Result
     */
    @Override
    public List<Novel> CurrentUserList() {
        //得到当前用户id
        Long userId = UserInfoUtil.getUser().getId();
        //从redis中查询数据
        List<Novel> novelList = new ArrayList<>();
        String key = RedisConstant.BACHONG_NOVEL_INFO + ":" + userId;
        Set<String> novelSet = stringRedisTemplate.opsForZSet().range(key, 0, -1);
        //如果没查到，则到数据库中进行查找（正常情况不会查数据库，只有当redis出现异常才会走数据库）
        if (novelSet == null || novelSet.isEmpty()) {
            novelList = worksNovelMapper.selectByUserId(userId);
            //保存到redis当中
            for (Novel novel : novelList) {
                String json = JSON.toJSONString(novel);
                stringRedisTemplate.opsForZSet().add(key,json,novel.getId());
            }
        } else {
            //redis当中有数据
            for (String novelJson : novelSet) {
                //解析json
                Novel novel = JSON.parseObject(novelJson, Novel.class);
                novelList.add(novel);
            }
        }
        return novelList;
    }

    /**
     * 根据小说id获取小说的基本信息
     * @param id :
     * @return Result
     */
    @Override
    public NovelVo getNovelById(Long id) {
        NovelVo vo = new NovelVo();
        Novel novel = null;
        //得到当前用户id
        Long userId = UserInfoUtil.getUser().getId();
        //定义key
        String key = RedisConstant.BACHONG_NOVEL_INFO + ":" + userId;
        String detailKey=RedisConstant.BACHONG_NOVEL_DETAILS+":"+id;
        //从redis中根据score查询数据
        Set<String> set = stringRedisTemplate.opsForZSet().rangeByScore(key, id, id);
        if (set == null || set.isEmpty()) {
            //redis当中不包含当前数据，从数据库中查询相应数据
            novel=worksNovelMapper.selectById(id);
        }else {
            //解析json字符串
            String json = new ArrayList<>(set).get(0);
            novel = JSON.parseObject(json, Novel.class);
        }
        BeanUtils.copyProperties(novel, vo);
        List<NovelDetails> details=getDetails(detailKey,id);
        vo.setDetails(details);
        return vo;
    }

    /**
     * 获取小说详情信息
     * @param detailKey:
     * @return List<NovelDetails>
     * @author vermouth
     */
    private List<NovelDetails> getDetails(String detailKey,Long novelId) {
        Set<String> set = stringRedisTemplate.opsForZSet().range(detailKey, 0, -1);
        if (set!=null && !set.isEmpty()){
            //redis中存在相应数据
            List<NovelDetails> details=new ArrayList<>(set.size());
            for (String s : set) {
                NovelDetails novelDetails = JSON.parseObject(s, NovelDetails.class);
                details.add(novelDetails);
            }
            Collections.sort(details,(o1,o2)->o1.getSorted()-o2.getSorted());
            return details;
        }
        //redis中不存在相应数据
        List<NovelDetails> details=worksNovelDetailMapper.selectByNovelId(novelId);
        //保存到redis当中
        details.stream()
                .map(detail->
                        stringRedisTemplate.opsForZSet().add(detailKey, JSON.toJSONString(detail), detail.getId()));
        return details;
    }


    /**
     * 新增对应小说的小说章节
     * @param novelDetails :
     * @return Result
     */
    @Override
    public void saveNovelDetail(NovelDetails novelDetails) {
        //字段校验
        if (StrUtil.isBlank(novelDetails.getTitle())){
            throw new CustomException(ErrorCodeEnum.NOVEL_DETAIL_TITLE_ILLEGAL);
        }
        if (novelDetails.getNovelId()==null){
            throw new CustomException(ErrorCodeEnum.NOVEL_ID_ILLEGAL);
        }
        //利用全局id生成器生成小说详情id
        long detailId = globalIdWorker.generateId(RedisConstant.BACHONG_ID_NOVEL_DETAILS);
        novelDetails.setId(detailId);
        //补全小说详情的对应属性
        //利用redis生成每个小说对应章节的排序字段
        Long sorted = stringRedisTemplate.opsForValue().increment(RedisConstant.BACHONG_NOVEL_SORTED + novelDetails.getNovelId());
        novelDetails.setSorted(Integer.valueOf(sorted.toString()));
        novelDetails.setCreateTime(LocalDateTime.now());
        //插入到数据库当中
        worksNovelDetailMapper.insert(novelDetails);
        //存储到redis相应的集合里,只需要存储id和title即可，无需存储多余属性
        String detailKey=RedisConstant.BACHONG_NOVEL_DETAILS+":"+novelDetails.getNovelId();
        String json = JSON.toJSONString(novelDetails);
        stringRedisTemplate.opsForZSet().add(detailKey,json,novelDetails.getSorted());
    }

    /**
     * 删除小说章节
     * @param novelDetails :
     * @return Result
     */
    @Override
    public void removeNovelDetail(NovelDetails novelDetails) {
        //在redis集合中删除相应对象
        String detailKey=RedisConstant.BACHONG_NOVEL_DETAILS+":"+novelDetails.getNovelId();
//        Long remove = stringRedisTemplate.opsForZSet().remove(detailKey, json);
        Long remove = stringRedisTemplate.opsForZSet().removeRangeByScore(detailKey, novelDetails.getSorted(), novelDetails.getSorted());
        //在数据库中删除相应章节
        if (remove == null ) {
            throw new CustomException(ErrorCodeEnum.UNKNOW_ERROR);
        }
        worksNovelDetailMapper.deleteById(novelDetails.getId());
    }

    /**
     * 新增小说章节内容
     * @param novelDetails :
     * @return Result
     */
    @Override
    public void saveDetailContent(NovelDetails novelDetails) {
        //校验小说内容是否为空
        if (StrUtil.isBlank(novelDetails.getContent())){
            throw new CustomException(ErrorCodeEnum.NOVEL_DETAIL_CONTENT_ILLEGAL);
        }
        //根据id更新数据库中的内容
        worksNovelDetailMapper.update(novelDetails);
    }

    /**
     * 根据小说章节id获取小说章节内容
     * @param detailId :
     * @return Result
     */
    @Override
    public NovelDetails getDetailContent(Long detailId) {
        //根据detailId查询小说章节信息
        NovelDetails novelDetails=worksNovelDetailMapper.selectById(detailId);
        //判断是否为空，如果为空则抛出异常
        if (novelDetails==null){
            throw new CustomException(ErrorCodeEnum.NOVEL_DETAIL_NOT_EXISTS);
        }
        return novelDetails;
    }

    /**
     * 根据小说id删除小说
     * @param novelId :
     * @return Result
     */
    @Override
    @Transactional
    public void removeNovel(Long novelId) {
        //当前这样删除存在效率问题，可以利用异步消息队列实现异步删除，先删除redis，异步删除数据库
        //先删除redis当中的内容
        //在redis中删除存储章节的zset集合
        String detailKey=RedisConstant.BACHONG_NOVEL_DETAILS+":"+novelId;
        stringRedisTemplate.delete(detailKey);
        //在当前用户的小说里表中删除该小说
        Long userId = UserInfoUtil.getUser().getId();
        String key= RedisConstant.BACHONG_NOVEL_INFO + ":" + userId;
        stringRedisTemplate.opsForZSet().removeRangeByScore(key,novelId,novelId);
        //删除该小说的排序字段值
        String sortedKey=RedisConstant.BACHONG_NOVEL_SORTED +novelId;
        stringRedisTemplate.delete(sortedKey);
        //异步发送进行处理
        log.info("开始发送异步消息。。。");
        rabbitTemplate.convertAndSend(RabbitmqConfig.BACHONG_NOVEL_EXCHANGE,
                RabbitmqConfig.BACHONG_NOVEL_DELETE_KEY,
                novelId);
        log.info("异步消息发送成功");
/*        //在数据库当中删除与之相关的小说章节内容
        worksNovelDetailMapper.deleteByNovelId(novelId);
        //在数据库中删除小说
        worksNovelMapper.deleteById(novelId);
        //在redis中删除存储章节的zset集合
        String detailKey=RedisConstant.BACHONG_NOVEL_DETAILS+":"+novelId;
        stringRedisTemplate.delete(detailKey);
        //在当前用户的小说里表中删除该小说
        Long userId = UserInfoUtil.getUser().getId();
        String key= RedisConstant.BACHONG_NOVEL_INFO + ":" + userId;
        stringRedisTemplate.opsForZSet().removeRangeByScore(key,novelId,novelId);
        //删除该小说的排序字段值
        String sortedKey=RedisConstant.BACHONG_NOVEL_SORTED +novelId;
        stringRedisTemplate.delete(sortedKey);*/
    }

    /**
     * 修改小说发布状态
     * @param novelId:
     * @param status:
     * @return Result
     * @author vermouth
     */
    @Override
    public void updateNovelStatus(Long novelId,Short status) {
        //todo 之后可能会改，暂时想不到该做些什么
        //删除redis当中当前用户的数据
        String key=RedisConstant.BACHONG_NOVEL_INFO+":"+UserInfoUtil.getUser().getId();
        stringRedisTemplate.delete(key);
        //修改数据库中小说的状态
        Novel novel = new Novel();
        novel.setStatus(status);
        novel.setId(novelId);
        novel.setSubmitTime(LocalDateTime.now());
        worksNovelMapper.update(novel);
    }

    /**
     * 获取当前用户小说提交数据
     * @return Result
     */
    @Override
    public List<NovelRecordVo> getCurrentRecord() {
        //获取当前登录用户id
        Long userId = UserInfoUtil.getUser().getId();
        //根据userId查询当前用户已经提交的小说
        List<Novel> novelList=worksNovelMapper.selectRecord(userId);
        //封装返回数据
        List<NovelRecordVo> voList=new ArrayList<>();
        for (Novel novel : novelList) {
            NovelRecordVo vo = new NovelRecordVo();
            vo.setName(novel.getName());
            vo.setStatus(novel.getStatus());
            vo.setSubmitTime(novel.getSubmitTime());
            voList.add(vo);
        }
        return voList;
    }


    /**
     * 获取所有待审核状态的小说列表(status==1)
     * @return List<AuditNovelVo>
     */
    @Override
    public List<AuditNovelVo> getAudits() {
        List<AuditNovelVo> voList=worksNovelMapper.selectByStatus((short)1);
        return voList;
    }

    /**
     * 设置审核状态
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public boolean setAuditStatus(AuditNovelDto dto) {
        //这里可能存在多位审核人员同时审核同一篇小说的情况，可能结果也不同
        //这里才用乐观锁的方式
        //删除redis当中当前作者的所有小说缓存
        String key=RedisConstant.BACHONG_NOVEL_INFO+":"+dto.getUserId();
        stringRedisTemplate.delete(key);
        //修改数据库当中的小说状态，这里需使用乐观锁！！！！
        int num=worksNovelMapper.updateStatusOptim(dto);
        System.err.println(num);
        if (num != 1){
            throw new CustomException(ErrorCodeEnum.UNKNOW_ERROR);
        }
        //双删策略，确保数据的一致性
        stringRedisTemplate.delete(key);
        return true;
    }


    /**
     * 根据小说名获取小说id
     * @param novelName :
     * @return Long
     */
    @Override
    public List<Long> getNovelIdByNovelName(String novelName) {
        //数据校验
        if (StrUtil.isBlank(novelName)){
            throw new CustomException(ErrorCodeEnum.NOVEL_NAME_ILLEGAL);
        }
        //查询数据库
        List<Long>  novelIds=worksNovelMapper.selectByNovelName(novelName);
        if (novelIds.isEmpty()){
            throw new CustomException(ErrorCodeEnum.UNKNOW_ERROR);
        }
        return novelIds;
    }

    /**
     * 根据小说id获取小说名
     * @param novelId :
     * @return String
     */
    @Override
    public String getNovelNameById(Long novelId) {
        //数据库中查询小说名
        Novel novel = worksNovelMapper.selectById(novelId);
        //可能会被删除，判断一下是否为空
        if (novel==null){
            throw new CustomException(ErrorCodeEnum.UNKNOW_ERROR);
        }
        return novel.getName();
    }
}
